# -*- coding: ISO-8859-1 -*-
"""\
Convert data from the Engineering Village 2 web site to BibTeX format.

Engineering Village 2 (EV2) provides access to the most important engineering 
content through one single interface. With EV2 you can search databases
like Compendex and Inspec. Content from IEEE is also available. 

The bibliographic data available from EV2 is more detailed and more consistent
than for instance IEEEXplore. It's also easier to extract.

http://www.engineeringvillage2.org/

Currently only the detailed record view is supported.
"""

# Copyright (c) 2006 Kjell Magne Fauske
# See LICENSE for details.

import re
import bibtexbib
from converterbase import *
from biblibutils import *
from sets import Set


# regex for splitting each line into tag and data field
_tagdata_re = re.compile(r"\s*(.*?):\s*(.*)", 
                             re.MULTILINE)

# regex for splitting multiple records
_splitrecords_re = re.compile(r'^\s*\d+\.\s*$',re.MULTILINE)

_keyword_fields = ['ei controlled terms', 'uncontrolled terms',
    'ei classification codes','inspec controlled terms',
    'inspec classification codes']

# Map from EV2 months to standard BibTeX month macros
_monthmap = {
    'Jan' : 'jan', 'January' : 'jan', 'Feb':'feb', 'February' :'feb',
    'March': 'mar', 'Mar' : 'mar',
    'April' : 'apr', 'Apr' : 'apr', 'May' : 'may', 'June' : 'jun', 'Jun' : 'jun',
    'July' : 'jul', 'Jul' : 'jul', 'Aug' : 'aug', 'August' : 'aug',
    'Sep': 'sep', 'Sept' : 'sep', 'September' : 'Sep',
    'Oct' : 'oct', 'October' : 'oct', 'Nov' : 'nov', 'November' : 'nov', 
    'Des': 'dec', 'Dec' : 'dec', 'December' : 'dec',
}

def extractEV2Data(src):
    """Extract bibliographic data from a copied EV2 web page
    
    """    
    return extractDetailedView(src)
    
def extractDetailedView(src):
    """Create a dictionary from src
    
    All keys are converted to lower case. 
    """
    ev2entries = []
    records = _splitrecords_re.split(src)
    if len(records) > 1:
        records = records[1:]
    for record in records:
        fields = _tagdata_re.findall(record)
        ev2entry = {}
        for (tag, data) in fields:
            # remove whitespace
            # is the data of a keyword type?
            if data.find('|')>=0:
                if tag.lower() in _keyword_fields:
                    data = [item.strip() for item in data.split('|')]
            # is data of author type?
            elif data.find(';')>=0:
                if tag.lower() == 'authors':
                    data = [item.strip() for item in data.split(';')]
            ev2entry[tag.lower()] = data
        ev2entries.append(ev2entry)
    return ev2entries
    

class EV2Converter(ToBibTeXConverter):
    """Class for converting  a EV2 entry to BibTeX format
    
    Tries to solve the conversion problem without using an endless list of
    IFs and ELSEs.
    """
    def __init__(self, ev2entry, options = {}):
        # detect type
        # Todo: Need a more intelligent document type detection!
        # The 'document type' field is not always available
        doctype = ev2entry.get('document type','')
        if 'JA' in doctype:
            entrytype = 'article'
        elif 'CA' in doctype:
            entrytype = 'inproceedings'
        else:
            entrytype = 'article'
        ToBibTeXConverter.__init__(self, ev2entry, 
                bibtexbib.entrymap.get(entrytype,bibtexbib.misc_entry),options)
        self.bibtexentry.type = entrytype
        
    def abstractHandler(self):
        abstract =  self.getField('abstract')
        if abstract:
            self.bibtexentry.append('abstract', abstract)
            
    def addressHandler(self):
        address = self.getField('conference location')
        if address:
            self.bibtexentry.append('address', address)
            
    def authorHandler(self):
        author_fields = ['authors']
        authors, used_fields = getall(self.entry, author_fields)
        if authors:
            # Some authors have footnote numbers. Need to remove them
            # Need to find a more elegant solution! Will fail if there are
            # more than one footnote
            tmplist = []
            for author in authors:
                if author:
                    if author[-1].isdigit():
                        author = author[:-1]
                tmplist.append(author)
            authors = tmplist
            self.bibtexentry.append('author',authors)
            self.unused_fields -= used_fields
    
    def booktitleHandler(self):
        # this is a tricky field
        # Inspec and compendex use both the 'conference name' and
        # 'serial title' field. We should prefer the field starting with
        # 'Proceedings'
        # Todo: Proceedings naming issue
        bt_fields = ['serial title', 'conference name']
        booktitle, used_fields = getfirst(self.entry,bt_fields)
        if booktitle:
            self.bibtexentry.append('booktitle',booktitle)
            self.unused_fields -= used_fields
    
    def doiHandler(self):
        doi = self.getField('doi')
        if doi:
            self.bibtexentry.append('doi',doi)
    
    def ISSNHandler(self):
        issn = self.getField('issn')
        if issn:
            self.bibtexentry.append('ISSN',issn)
                    
    def journalHandler(self):
        # Todo: Include abbreviated serial title in the bibtex output?
        journal = self.getField('serial title')
        if journal:
            self.bibtexentry.append('journal',journal)
            
    def keywordsHandler(self):
        # Todo: Better handling of controlled leywords and user keywords
        # Elsevier operates with controlled keywords and user keywords. 
        # How should this be handled? Separate into two different fields?
        keywords, used_fields = getall(self.entry, ['ei controlled terms',
                                'inspec controlled terms','uncontrolled terms'])
        if keywords:
            self.bibtexentry.append('keywords',keywords)
            self.unused_fields -= used_fields    

    def languageHandler(self):
        lang = self.getField('language')
##        if lang:
##            # Todo: Use Babel def. of language name
##            self.bibtexentry.append('language',lang)       
     
    def monthHandler(self):
        # inspec puts the mont in the publication date field
        # while compendex use the issue data field.
        monthstr, tmp = getfirst(self.entry,['publication date',
            'issue date', 'conference date'])
        if monthstr:
            month = extStr(monthstr)
            # Convert to BibTeX month abbrev. 
            # Note that compendex uses abbreviated month names.
            if _monthmap.has_key(month):
                month = '@'+_monthmap[month]
            self.bibtexentry.append('month',month)
     
    def numberHandler(self):
        num = extNum(self.getField('issue'));
        if num: self.bibtexentry.append('number',num)

    def pagesHandler(self):
        pages = self.getField('pages')
        pages = extPages(pages)
        if pages:
            # Inspec sometimes use Pages: 111-18 How should this be handled?
            pages = "%s--%s" % pages
            self.bibtexentry.append('pages', pages)
        
    def volumeHandler(self):
        vol = extNum(self.getField('volume'));
        if vol: self.bibtexentry.append('volume',vol)

    def yearHandler(self):
        # check compendex first
        year = self.getField('publication year')
        if year:
            self.bibtexentry.append('year',year)
            return
        # check inspec
        year = self.getField('publication date')
        if year:
            year = extNum(year)
            self.bibtexentry.append('year',extNum(year))
                
    
    def titleHandler(self):
        # get title
        title, used_fields = getfirst(self.entry, ['title'],'')
        if title:
            self.bibtexentry.append('title',title)
            self.unused_fields -= used_fields        
  
def convertFromString(src, options={}):
    """Extract data from src and return a list of BibTeX entries.
    
    Input:
        scr - Raw text usually pasted from the clipboard.
        options - A dict with various conversion options.
    Output:
        A list of BibTeXEntry classes.
    """
    ev2entries = extractEV2Data(src)
    if not ev2entries: return None
    entries = []
    for entry in ev2entries:
        t = EV2Converter(entry, options)
        t.convert()
        entries.append(t.bibtexentry)
    return entries

    
if __name__ == '__main__':
    import testdata
    ev2entry = extractEV2Data(testdata.testdata4)[0]
    options = {}
    options['generatebibkey'] = 1
    options['braceuppercase'] = True
    t = EV2Converter(ev2entry, options)
    t.convert()
    print t.bibtexentry.toString()